package com.ruoyi.business.service.impl.loan;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ruoyi.bizsys.domain.*;
import com.ruoyi.bizsys.dto.PayTrans;
import com.ruoyi.bizsys.dto.UserInfo;
import com.ruoyi.bizsys.enums.AwardEventEnum;
import com.ruoyi.bizsys.enums.EnumFeeType;
import com.ruoyi.bizsys.enums.RealNameStatusEnum;
import com.ruoyi.bizsys.mapper.LoanIouMapper;
import com.ruoyi.bizsys.mapper.RealNameAuthMapper;
import com.ruoyi.bizsys.service.*;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.bizsys.utils.UserCacheService;
import com.ruoyi.business.common.AsyncService;
import com.ruoyi.business.common.config.ProtocolConfig;
import com.ruoyi.business.common.riskRule.RulePremiseService;
import com.ruoyi.business.common.riskRule.RuleStrategy;
import com.ruoyi.business.component.MqProduce;
import com.ruoyi.business.component.mqconsumer.LoanResultConsume;
import com.ruoyi.business.service.*;
import com.ruoyi.business.util.PdfUtils;
import com.ruoyi.common.constant.LoanConstants;
import com.ruoyi.common.constant.RiskConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.enums.UserTagEnum;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.Func;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.sms.domain.SmsSendData;
import com.ruoyi.sms.service.ISmsService;
import com.ruoyi.system.domain.SysFiles;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysFilesService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;

/**
 * @Author: cyh
 * 放款
 *
 *
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class LoanServiceImpl implements ILoanService {

    private final ILoanOrderService loanOrderService;

    private final AsyncService asyncService;

    private final ICustomerInfoService customerInfoService;

    private final RestTemplateService restTemplateService;

    private final ICustBankCardService custBankCardService;

    private final ICreditAccountService creditAccountService;

    private final ICreditAccountInfoService creditAccountInfoService;

    private final IRealNameAuthService realNameAuthService;

    private final IAppUserService appUserService;

    private final IPayTransService payTransService;

    private final ISmsService smsService;

    private final ProtocolConfig contractConfig;

    private final ISysFilesService sysFilesService;

    @Autowired
    @Lazy
    private final IBLoanInfoService bLoanInfoService;

    private final ISysDictDataService sysDictDataService;

    private final ILoanIouService loanIouService;

    private final IIncreasedLimitLogService increasedLimitLogService;

    private final ICustomerAndonService customerAndonService;

    private final ICreditFlagService creditFlagService;

    private final RulePremiseService rulePremiseService;

    private final INoticeSmsService noticeSmsService;

    private final RedisCache redisCache;

    private final MqProduce mqProduce;

    private final IAccountFlowService accountFlowService;

    private final IAwardDistributionService awardDistributionService;

    private final UserCacheService userCacheService;

    private final LoanIouMapper loanIouMapper;

    private final RealNameAuthMapper realNameAuthMapper;

    private final ISysConfigService sysConfigService;

    private static final DecimalFormat DATE_LATE_FEE_FORMAT = new DecimalFormat("0.##########");

    /**
     * 满足放款条件，请求风控引擎
     *
     * @param applySerialNo
     */
    @Override
    public void rulePass(String applySerialNo) {
        log.info("applySerialNo:{}", applySerialNo);
        LoanOrder loanOrder = loanOrderService.getOneBySerialNo(applySerialNo);
        if (Func.isEmpty(loanOrder)) return;
        log.info("提现申请队列消费,LoanOrderStatus:{}", loanOrder.getLoanStatus());
        //近处理审批中的订单
        if (LoanOrder.LOAN_STATUS_9_APPROVALING != loanOrder.getLoanStatus()) return;
        //规则引擎
        JSONObject ruleRst = restTemplateService.riskRuleEngine(RiskConstants.RULE_TYPE_LOAN, loanOrder.getLoanSerialNo());
        boolean ruleResult = handleLoanRuleRst(loanOrder, ruleRst);
        if (ruleResult) {
            //放款
            loanPayTransBoard(loanOrder.getLoanSerialNo());
        }
    }

    /**
     * 提现结果处理
     *
     * @param loanOrder
     * @param ruleRst
     */
    public boolean handleLoanRuleRst(LoanOrder loanOrder, JSONObject ruleRst) {
        log.info("loanOrder:{},ruleRst:{}", loanOrder, ruleRst.getString(RiskConstants.RULE_RESULT_DEAL_TYPE_KEY));
        String riskResult = ruleRst.getString(RiskConstants.RULE_RESULT_DEAL_TYPE_KEY);
        //中断
        if (StringUtils.equals(riskResult, RiskConstants.RULE_RESULT_REVOKE)) return false;
        //保存风控需要的字段到客户信息表
        creditAccountInfoService.updateCustomerByRuleResult(ruleRst, loanOrder.getCustNo(), RiskConstants.RULE_TYPE_LOAN);
        //未成功
        if (handleLoanUnsuccessful(loanOrder, ruleRst)) return false;
        //未知结果
        if (!riskResult.equals(RiskConstants.RULE_RESULT_PASS)) return false;
        //审批通过
        LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId()).chainLoanStatus(LoanOrder.LOAN_STATUS_1_APPROVE_PASSED).chainRemark("rule pass");
        return loanOrderService.updateById(orderUpdate);
    }


    /**
     * 放款成功
     * 内部类调用必须通过这个方法，否则异步、事务不生效
     */
    private void loanPayTransBoard(String applySerialNo) {
        SpringUtils.getBean(LoanServiceImpl.class).loanPayTrans(applySerialNo);
    }

    /**
     * 放款开始
     *
     * @param applySerialNo
     */
    @Async("threadPoolTaskExecutor")
    @Override
    public void loanPayTrans(String applySerialNo) {
        LoanOrder loanOrder = loanOrderService.getOneBySerialNo(applySerialNo);
        if (Func.isEmpty(loanOrder)) return;
        try {
            //查询资金是否满足，不满足，更新提现状态，并提示付款失败  超时重试三次
            //payTransService.tryFunc(e -> checkPayAccountBalance(loanOrder));
            //生成贷款合同
            String contractNo = createContractPdf(loanOrder);
            //合同生成状态
            LoanOrder newOrder = new LoanOrder().chainLoanStatus(LoanOrder.LOAN_STATUS_7_CONTRAC_GENERATED).chainRemark("Created Contract").chainContractNo(contractNo);
            newOrder.setId(loanOrder.getId());
            loanOrderService.updateById(newOrder);
            //开始放款
            startLending(loanOrder);
        } catch (Exception e) {
            if (e instanceof BusinessException) {
                BusinessException be = (BusinessException) e;
                log.error("提现异常，msg = {}, details = {}", be.getMessage(), be.getDetails());
            } else {
                log.info("提现异常 error：{}", e);
            }

            //余额不足 不做处理
            if (loanOrder.getLoanStatus() == LoanOrder.LOAN_STATUS_2_INSUFFICIENT_ACCOUNT_BALANCE) return;
            //String userId = customerInfoService.getUserIdByCustNo(loanOrder.getCustNo());
            asyncService.apdUserTag(AppUserMark.builder().userId(customerInfoService.getUserIdByCustNo(loanOrder.getCustNo())).secondTag(e.getMessage()).build(), UserTagEnum.getDiy("Withdrawal failed", e.getMessage()));
        }
    }

    /**
     * 未成功的提现申请处理
     *
     * @param loanOrder
     * @param ruleRst
     * @return
     */
    private boolean handleLoanUnsuccessful(LoanOrder loanOrder, JSONObject ruleRst) {
        String riskResult = ruleRst.getString(RiskConstants.RULE_RESULT_DEAL_TYPE_KEY);
        String userId = customerInfoService.getUserIdByCustNo(loanOrder.getCustNo());
        //拒绝
        if (StringUtils.equals(riskResult, RiskConstants.RULE_RESULT_REJECT)) {
            loanOrder.setFreezeDate(ruleRst.getString(RiskConstants.RULE_RESULT_LOAN_FREEZE));
            updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_0_APPROVE_REFUSED, "rule reject");
            asyncService.apdUserTag(AppUserMark.builder().userId(userId).build(), UserTagEnum.LOAN_REJECT);
            return true;
        }
        //关闭
        if (RiskConstants.RULE_RESULT_CLOSE.equals(riskResult)) {
            updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_8_CLOSE, ruleRst.getString(RiskConstants.RULE_RESULT_CLOSE_REASON));
            sendLoanCloseSms(userId);
            asyncService.apdUserTag(AppUserMark.builder().userId(userId).build(), UserTagEnum.LOAN_FAILED_WITHDRAWAL_CANCEL);
            return true;
        }
        return false;
    }

    @Override
    public void startLending(LoanOrder loanOrder) {
        log.info("startLending方法开始放款,入参loanOrder{}", loanOrder);
        //当前创建的流水号不存在则走放款接口流程。如果存在 需要再校验放款状态 状态未查得 才走接口流程
        boolean transSerialIsActive = true;
        if (StringUtils.isBlank(loanOrder.getPayTransSerialNo())) {
            //创建放款流水号
            loanOrder.setPayTransSerialNo(IdUtils.serialNo(BizConstants.ACTION_TYPE_LOAN));
            loanOrderService.update(new UpdateWrapper<LoanOrder>().set("pay_trans_serial_no", loanOrder.getPayTransSerialNo()).eq("id", loanOrder.getId()));
        } else {
            //放款流水号是否存在于payStack端
            transSerialIsActive = verifyTransfer(loanOrder, true);
        }
        //流水号已存在
        if (!transSerialIsActive) return;

        payTrans(loanOrder);
        updateLoanOrder(loanOrder.getId(), LoanOrder.LOAN_STATUS_3_LOAN_PENDING, "loan pending");
        verifyTransfer(loanOrder, false);
    }

    /**
     * 放款接口调用处理
     *
     * @param loanOrder
     */
    private void payTrans(LoanOrder loanOrder) {
        BankCode bankCode = bLoanInfoService.getBanckInfoByAccountNumber(loanOrder.getAccountNumber());
        try {
            CustomerInfo customerInfo = customerInfoService.getOneByCustNo(loanOrder.getCustNo());
            PayTrans payTrans = PayTrans.builder()
                    .accountNumber(loanOrder.getAccountNumber())
                    .bankCode(bankCode.getPaystackBankCode())
                    .loanAmt(loanOrder.getLoanAmt())
                    .payTransSerialNo(loanOrder.getPayTransSerialNo())
                    .userId(customerInfo.getUserId())
                    .custNo(customerInfo.getCustNo())
                    .build();
            payTransService.payTrans(payTrans);
        } catch (Exception e) {
            if (e instanceof BusinessException) {
                BusinessException be = (BusinessException) e;
                log.error("放款交易处理异常，code = {}, msg = {}, details = {}", be.getCode(), be.getMessage(), be.getDetails());

                //放款号已存在,不改变订单状态
                if (RspEnmu.REFERENCE_ALREADY_EXISTS_ON_TRANSFER.getCode().equals(be.getCode())) throw e;
                //失败
                if (RspEnmu.ACCOUNT_NUMBER_IS_INVALID.getMessage().equals(e.getMessage())) {
                    updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_5_LOAN_FAILED, be.getDetails());
                }
                // message=Your balance is not enough to fulfil this request/You cannot initiate third party payouts at this time时，修改loanOrder订单状态为放款等待（loan_status=6）；
                // 通过定时任务触发+财务触发
                if (RspEnmu.INSUFFICIENT_WALLET_BALANCE.getMessage().equals(be.getMessage())) {
                    updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_6_RE_INITIATE_PAY, be.getDetails());
                }
//                //如果不是超时和异常,那么均是失败
//                if (!RspEnmu.NETWORK_BUSY.getCode().equals(be.getCode())) {
//                    updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_5_LOAN_FAILED, be.getDetails());
//                }

            }
            throw e;
        }
    }

    @Override
    public boolean verifyTransfer(LoanOrder loanOrder, boolean isRetryTask) {

        JSONObject verifyTransRes = getVerifyTransferRes(loanOrder, isRetryTask);
        if (verifyTransRes == null) return true;

        // 接收代付结果，代付失败，查询是否重新代付开关，无需则修改提现状态为放款失败，需则另行重新触发
        String transStatus = verifyTransRes.getString("status");
        log.info("loanOrder:{} 放款结果：{}", loanOrder.getLoanSerialNo(), transStatus);

        if (transStatus.equals("pending") || transStatus.equals("otp")) {
            updateLoanOrder(loanOrder.getId(), LoanOrder.LOAN_STATUS_3_LOAN_PENDING, transStatus);
        } else if (!isRetryTask) {
            //不是定时任务，则发送消息
            String result = transStatus.equals("failed") ? LoanResultConsume.FAILED : LoanResultConsume.SUCCESS;
            mqProduce.sendLoanResultOrderly(loanOrder.getLoanSerialNo(), result, loanOrder.getCustNo());
        }
        return false;
//        if (transStatus.equals("failed")) {
//            dealFailedLoan(loanOrder);
//            return false;
//        }
//        dealSuccessLoanBoard(loanOrder);
//        return false;
    }

    /**
     * 放款最终结果处理
     *
     * @param loanSerialNo
     * @param result
     */
    @Override
    public void dealLoanResult(String loanSerialNo, String result) {
        log.info("放款结果处理开始 >>> loanSerialNo = {}, result = {}", loanSerialNo, result);
        //状态456排除
        LoanOrder loanOrder = loanOrderService.getOneBySerialNo(loanSerialNo);
        if (!dealLoanResultCheck(loanOrder.getLoanStatus())) return;
        if (result.equals(LoanResultConsume.FAILED)) {
            dealFailedLoan(loanOrder);
        }
        if (result.equals(LoanResultConsume.SUCCESS)) {
            dealSuccessLoanBoard(loanOrder);
        }
    }

    private boolean dealLoanResultCheck(Integer loanOrderStatus) {
        if (loanOrderStatus == LoanOrder.LOAN_STATUS_4_LOAN_SUCCESS ||
                loanOrderStatus == LoanOrder.LOAN_STATUS_5_LOAN_FAILED ||
                loanOrderStatus == LoanOrder.LOAN_STATUS_6_RE_INITIATE_PAY) {
            log.info("放款结果处理结束 >>> loanOrderStatus = {}", loanOrderStatus);
            return false;
        }
        return true;
    }


//    @Override
//    public boolean verifyTransfer(LoanOrder loanOrder, boolean isRetryTask) {
//
//        JSONObject verifyTransRes = getVerifyTransferRes(loanOrder, isRetryTask);
//        if (verifyTransRes == null) return true;
//
//        // 接收代付结果，代付失败，查询是否重新代付开关，无需则修改提现状态为放款失败，需则另行重新触发
//        String transStatus = verifyTransRes.getString("status");
//        log.info("代付结果：{}", transStatus);
//        if (transStatus.equals("pending") || transStatus.equals("otp")) {
//            updateLoanOrder(loanOrder.getId(), LoanOrder.LOAN_STATUS_3_LOAN_PENDING, transStatus);
//            return false;
//        }
//        if (transStatus.equals("failed")) {
//            dealFailedLoan(loanOrder);
//            return false;
//        }
//        dealSuccessLoanBoard(loanOrder);
//        return false;
//    }

    /**
     * 调用交易查询结果
     * 处理异常情况
     *
     * @param loanOrder
     * @param isRetryTask
     * @return
     */
    private JSONObject getVerifyTransferRes(LoanOrder loanOrder, boolean isRetryTask) {
        try {
            return payTransService.verifyTransfer(loanOrder.getPayTransSerialNo());
        } catch (BusinessException e) {
            //定时任务-放款流水号不存在时返回null调用后续的放款接口;其他异常抛出
            if (isRetryTask) {
                if (RspEnmu.TRANSACTION_SERIAL_NO_NOT_EXISTS.getCode().equals(e.getCode())) {
                    return null;
                }
                throw new BusinessException(RspEnmu.VERIFY_TRANSFER_NETWORK_ERROR);
            }
            if (RspEnmu.EXTERNAL_API_RSP_NULL.getCode().equals(e.getCode())) {
                throw e;
            }
//            if (RspEnmu.TRANSACTION_SERIAL_NO_NOT_EXISTS.getCode().equals(e.getCode())) {
//                updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_5_LOAN_FAILED, e.getDetails());
//                throw e;
//            }
            if (RspEnmu.TRANSACTION_SERIAL_NO_NOT_EXISTS.getCode().equals(e.getCode())) {
                return null;
            }
            if (!RspEnmu.NETWORK_BUSY.getCode().equals(e.getCode())) {
                updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_5_LOAN_FAILED, e.getDetails());
            }
            // updateLoanOrder(loanOrder.getId(), LoanOrder.LOAN_STATUS_3_LOAN_PENDING, e.getDetails());
            throw new BusinessException(RspEnmu.LOAN_FAILED);
        }
    }

    /**
     * 放款成功
     * 内部类调用必须通过这个方法，否则事务不生效
     *
     * @param loanOrder
     */
    private void dealSuccessLoanBoard(LoanOrder loanOrder) {
        LoanServiceImpl bean = SpringUtils.getBean(LoanServiceImpl.class);
        bean.dealSuccessLoan(loanOrder);
        // 短信通知
        // TODO延期产品   提现成功
        Date createTime = DateUtils.afterXHourToNowDate(loanOrder.getCreateTime(),1);
        if(createTime.before(new Date())){
            // 延迟放款就发 致歉放款延迟短信
            asyncService.withdrawLoanDelay(loanOrder.getCustNo(),loanOrder.getLoanAmt());
        }else {
            noticeSmsService.sendLoanNotice(loanOrder, BizConstants.SMS_TYPE_LOAN_SUCCESS);
        }

    }


    @Override
    @Transactional
    public void dealSuccessLoan(LoanOrder loanOrder) {

        //非当日则重新创建合同并更新
        if (Func.isEmpty(loanOrder.getContractNo()) || DateUtils.daysBetween(loanOrder.getCreateTime(), new Date()) > 0) {
            loanOrder.setContractNo(createContractPdf(loanOrder));
        }
        // 解冻并减少提现金额
        updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_4_LOAN_SUCCESS, "loan success");
        // 初始化借据
        loanIouService.initializeIOUByOrder(loanOrder);

        CustomerInfo customerInfo = customerInfoService.getOneByCustNo(loanOrder.getCustNo());
        //更新账户汇总信息
        CreditAccount account = creditAccountService.getByCustNo(customerInfo.getCustNo());

        //更新账户额度
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + customerInfo.getCustNo();
        redisCache.functionLock(e -> bLoanInfoService.updateAccount(account), lockKey);

        //帐目流水
        createAccountFlow(loanOrder);
        //通知app 不存在则新增
        //iAppUserPushService.appUserPush(customerInfo.getUserId(), AppUserPush.PUSH_TYPE_SCORE);

        //用户标签
        //userLoanSuccessMark(customerInfo, account);
        asyncService.apdUserTag(AppUserMark.builder().userId(customerInfo.getUserId())
                        .creditLine(account.getCreditLine())
                        .loanQuota(account.getLoanQuota())
                        .increasedAmt(increasedLimitLogService.getIncreasedAmtByCust(account.getCustNo()).getIncreasedAmt()).build(),
                custBankCardService.count(new QueryWrapper<CustBankCard>().eq("cust_no", account.getCustNo())) > 0 ? UserTagEnum.LOAN_SUCCEED_BIND_CARD : UserTagEnum.LOAN_SUCCEED_NOT_CARD);

        //分销奖励活动
        UserInfo userInfo = appUserService.getUserInfoByCustNo(loanOrder.getCustNo());
        awardDistributionService.distributionAwardTrigger(AwardEventEnum.WITHDRAW_SUCCESS, userInfo.getUserId(), loanOrder.getLoanAmt());

    }


    private void createAccountFlow(LoanOrder loanOrder) {
        AccountFlow accountFlow = new AccountFlow();
        accountFlow.setCustNo(loanOrder.getCustNo());
        accountFlow.setFeeType(EnumFeeType.LOAN_AMT.getCode());
        accountFlow.setAmt(loanOrder.getLoanAmt());
        accountFlow.setRelativeSerialNo(loanOrder.getLoanSerialNo());
        accountFlow.setReference(loanOrder.getPayTransSerialNo());
        accountFlow.setTransferChannel("PayStack");
        accountFlow.setTransferTime(loanOrder.getCreateTime());
        accountFlow.setLoanTime(loanOrder.getCreateTime());
        accountFlow.setDataSource("loan_iou");
        accountFlowService.insertByIndex(accountFlow);

        accountFlow.setFeeType(EnumFeeType.LOAN_PRINCIPAL.getCode());
        accountFlowService.insertByIndex(accountFlow);

        accountFlow.setFeeType(EnumFeeType.SERVICE_FEE.getCode());
        accountFlow.setAmt(loanOrder.getServiceFee());
        accountFlowService.insertByIndex(accountFlow);

        accountFlow.setFeeType(EnumFeeType.LOAN_INTEREST.getCode());
        accountFlow.setAmt(loanOrder.getInterestAmt());
        accountFlowService.insertByIndex(accountFlow);
    }


    //用户标签-提现成功
//    private void userLoanSuccessMark(CustomerInfo customerInfo, CreditAccount account) {
//        IncreasedLimitLog increasedAmtByCust = increasedLimitLogService.getIncreasedAmtByCust(account.getCustNo());
////        BigDecimal bg = increasedLimitLogService.getIncreasedAmtByCust(account.getCustNo());
//        AppUserMark appUserMark = AppUserMark.builder().userId(customerInfo.getUserId())
//                .creditLine(account.getCreditLine()).loanQuota(account.getLoanQuota()).increasedAmt(increasedAmtByCust.getIncreasedAmt()).build();
//        int cardCount = custBankCardService.count(new QueryWrapper<CustBankCard>().eq("cust_no", account.getCustNo()));
//        asyncService.apdUserTag(appUserMark, cardCount > 0 ? UserTagEnum.LOAN_SUCCEED_BIND_CARD : UserTagEnum.LOAN_SUCCEED_NOT_CARD);
//    }

    /**
     * 提现失败
     *
     * @param loanOrder
     */
    @Override
    public void dealFailedLoan(LoanOrder loanOrder) {
        if (isReInitiatePay()) {
            updateLoanOrder(loanOrder.getId(), LoanOrder.LOAN_STATUS_6_RE_INITIATE_PAY, "re-initiate pay");
            return;
        }
        updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_5_LOAN_FAILED, "loan failure");
        noticeSmsService.sendLoanNotice(loanOrder, BizConstants.SMS_TYPE_LOAN_FAILED);
    }

    private boolean isReInitiatePay() {
        return Integer.valueOf(sysDictDataService.selectDictLabel("biz_switch", "is_reinitiate_pay")) == 1;
    }

    /**
     * 创建合同
     *
     * @param loanOrder
     * @return
     */
    @Override
    public String createContractPdf(LoanOrder loanOrder) {
        String contractNo = IdUtils.generateContractNo();
        Map<String, String> map = buildPdfParam(contractNo, loanOrder);
        SysFiles files = PdfUtils.createLoanContractPdf(contractNo, contractConfig.getLoanContractTemplate(), map);
        sysFilesService.insertSysFiles(files);
        return contractNo;
    }

    private Map<String, String> buildPdfParam(String contractNo, LoanOrder loanOrder) {
        RealNameAuth realNameAuth = realNameAuthService.selectRealNameAuthByCustNo(loanOrder.getCustNo());
        Map<String, String> map = new HashMap<>();
        map.put("contractNo", contractNo);
        map.put("firstName", realNameAuth.getFirstName());
        map.put("middleName", realNameAuth.getMiddleName());
        map.put("lastName", realNameAuth.getLastName());
        map.put("birthDate", realNameAuth.getBirthDate());
        map.put("accountNumber", loanOrder.getAccountNumber());
        map.put("loanAmount", loanOrder.getLoanAmt().toString());
        map.put("interestFee", loanOrder.getInterestAmt().toString());
        map.put("serviceFee", loanOrder.getServiceFee().toString());
        map.put("repaymentDate", loanOrder.getRepayDate());
        BigDecimal dateLateFee = loanOrder.getDateLateFee().multiply(new BigDecimal("100"));
        String percent = DATE_LATE_FEE_FORMAT.format(dateLateFee);
        map.put("penalty", percent + "%");
        return map;
    }

    /**
     * 创建子借据合同
     *
     * @param minRepayAmt
     * @param loanIou
     * @param loanIouDeferred
     */
    @Override
    @Async("threadPoolTaskExecutor")
    public void createDeferredContractPdf(BigDecimal minRepayAmt, LoanIou loanIou, LoanIouDeferred loanIouDeferred) {
        log.info("开始创建延期借据合同 >>> minRepayAmt = {}，loanIou = {}，loanIouDeferred = {}", minRepayAmt, loanIou, loanIouDeferred);
        Map<String, String> map = buildPdfParamForloanDeferredOrder(minRepayAmt, loanIou, loanIouDeferred);
        SysFiles files = PdfUtils.createLoanContractPdf(loanIouDeferred.getContractNo(), contractConfig.getLoanDeferContractTemplate(), map);
        sysFilesService.insertSysFiles(files);
        log.info("创建延期借据合同结束 >>>");
    }

    private Map<String, String> buildPdfParamForloanDeferredOrder(BigDecimal minRepayAmt, LoanIou loanIou, LoanIouDeferred loanIouDeferred) {
        RealNameAuth realNameAuth = realNameAuthService.selectRealNameAuthByCustNo(loanIou.getCustNo());
        Map<String, String> map = new HashMap<>();
        //合同编号
        map.put("contractNo", loanIouDeferred.getContractNo());
        //客户姓名
        map.put("fullName", realNameAuth.getFullName());
        map.put("iouSignDate", DateUtils.multipleParse(DateUtils.dateTime(loanIou.getCreateTime()), DateUtils.DD_MM_YYYY));
        map.put("loanPrincipal", loanIou.getLoanPrincipal().setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        map.put("iouRepayDate", loanIou.getRepayDate());
        map.put("deferIouRepayDate", loanIou.getDeferredRepayDate());
        map.put("deferIouSignDate", DateUtils.multipleParse(DateUtils.dateTime(loanIouDeferred.getCreateTime()), DateUtils.DD_MM_YYYY));
        map.put("minRepayAmt", minRepayAmt.setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        map.put("repaymentPrincipal", ((loanIou.getLoanPrincipal()).subtract(loanIou.getRepaidPrincipal())).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        map.put("deferredPeriodNum", loanIouDeferred.getPeriodNum().toString());
        map.put("deferredInterest", loanIouDeferred.getInterestFee().setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        map.put("deferredServiceFee", loanIouDeferred.getServiceFee().setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        return map;

//        {延期合同签署时间}：延期借据创建时间，日-月-年（目前缺少）
//        {客户姓名}：客户的姓名，全名（目前是分开的）
//        {原贷款签署时间}：原借据的创建时间
//        {贷款金额}：原借据贷款本金
//        {原贷款到期时间}：原借据的还款时间（目前缺少）
//        {延期还款日期}：延期还款到期日期
//        {最低还款金额}：延期支付金额
//        {剩余金额}：剩余待还本金
//        {延期周期}：延期期数（目前显示为0）
//        {延期利息}：延期利息金额
//        {延期服务费}：延期服务费金额
    }

    /**
     * 检查提现订单余额
     *
     * @param loanOrder
     */
    private void checkPayAccountBalance(LoanOrder loanOrder) {
        if (payTransService.isPayAccountBalanceSufficient(loanOrder.getLoanAmt())) {
            updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_2_INSUFFICIENT_ACCOUNT_BALANCE, RspEnmu.INSUFFICIENT_ACCOUNT_BALANCE.getMessage());
            noticeSmsService.sendLoanNotice(loanOrder, BizConstants.SMS_TYPE_BALANCE_INSUFFICIENT);
            throw new BusinessException(RspEnmu.INSUFFICIENT_ACCOUNT_BALANCE);
        }
    }


    /**
     * 提现关闭短信
     *
     * @param userId
     */
    private void sendLoanCloseSms(String userId) {
        RealNameAuth realNameAuth = realNameAuthService.getOneByUserId(userId);
        JSONObject smsParam = new JSONObject();
        smsParam.put("lastName", realNameAuth.getLastName());
        SmsSendData smsData = SmsSendData.builder().noticeType(BizConstants.SMS_NOTICE_TYPE_NOTICE).smsType(BizConstants.SMS_TYPE_CUSTRVWRESULT_CLOSE).countryCode("+234").phoneNumber(realNameAuth.getPhoneNumber()).smsParam(smsParam).build();
        smsService.sendSms(smsData);
    }

    /**
     * 解冻提现冻结金、更新提现状态
     *
     * @param loanOrder
     * @param loanStatus
     * @param errMsg
     */
    @Transactional
    @Override
    public void updateFrozenAndLoanStatus(LoanOrder loanOrder, Integer loanStatus, String errMsg) {
        String custNo = loanOrder.getCustNo();
        BigDecimal loanAmt = loanOrder.getLoanAmt();
        //更新提现订单
        LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId()).chainLoanStatus(loanStatus).chainRemark(errMsg).chainFreezeDate(loanOrder.getFreezeDate());
        loanOrderService.updateById(orderUpdate);
        //解除冻结额度加锁执行
        // TODO: 2022/4/26  update where
        creditAccountService.unfreezeQuota(custNo, loanAmt);

    }

    /**
     * 更新订单LoanStatus、errMsg、errMsg
     *
     * @param loanOrderId
     * @param loanStatus
     * @param errMsg
     */
    private boolean updateLoanOrder(long loanOrderId, Integer loanStatus, String errMsg) {
        return loanOrderService.updateById(new LoanOrder().chainId(loanOrderId).chainLoanStatus(loanStatus).chainRemark(errMsg));
    }

    /**
     * 再次发起(手动发起)
     *
     * @param loanOrder
     * @return
     */
    @Override
    public String againTrigger(LoanOrder loanOrder) {
        //获取用户信息
        RealNameAuth realNameAuth = realNameAuthService.getOneByCustNo(loanOrder.getCustNo());
        AppUser appUser = appUserService.getOneByUserId(realNameAuth.getUserId());
        //前置校验
        CreditFlag creditFlag = creditFlagService.getOne(loanOrder.getLoanSerialNo(), RuleStrategy.LOAN);
        //再次校验各个标签
        rulePremiseService.reviewCreditFlagAgain(creditFlag, appUser.getUserUuid());
        //标签缺失
        if (Func.hasEmpty(creditFlag.getOrderFlag(), creditFlag.getDeviceFlag(), creditFlag.getAppsFlag(), creditFlag.getContactsFlag())) {
            //解冻冻结金额 更新状态
            updateFrozenAndLoanStatus(loanOrder, LoanOrder.LOAN_STATUS_10_CANCEL, "CreditFlag has Empty");
            //发送邮件
            customerAndonService.sendAndonEamil(creditFlag, appUser.getPhone(), realNameAuth.getFullName());
            //发送取消短信
            sendCancelSMS(loanOrder, realNameAuth, appUser);
            //更改用户标签
            asyncService.apdUserTag(AppUserMark.builder().userId(realNameAuth.getUserId()).build(), UserTagEnum.LOAN_FAILED);
            return "false";
        }
        //通过
        return againTriggerPass(loanOrder, realNameAuth, appUser, creditFlag);
    }

    @Override
    public void checkWalletWithdrawIouStatus() {
        log.info("进入钱包提现借据到期状态校验 >>> ");
        String custNo = userCacheService.getUserCacheInfo().getCustNo();
        LoanIou loanIou = loanIouService.getOne(new QueryWrapper<LoanIou>().eq("cust_no", custNo).eq("state", BizConstants.BIZ_STATUS_1).in("iou_status", LoanIou.LOAN_IOU_STATUS_NORMAL, LoanIou.LOAN_IOU_STATUS_OVERDUE).select("is_deferred", "iou_status", "loan_serial_no", "repay_date", "deferred_repay_date").orderByDesc("id").last("limit 1"));
        if (loanIou == null) {
            return;
        }

        log.info("最近一笔借据：iouStatus = {}, isDeferred = {}, repayDate = {}, deferredRepayDate = {}", loanIou.getIouStatus(), loanIou.getIsDeferred(), loanIou.getRepayDate(), loanIou.getDeferredRepayDate());

        if (loanIou.getIouStatus() == 2) {
            throw new BusinessException(RspEnmu.WALLET_WITHDRAW_IOU_OVERDUE);
        }

        try {
            String repayDate = loanIou.getIsDeferred() == 1 ? loanIou.getDeferredRepayDate() : loanIou.getRepayDate();
            int dateDiff = DateUtils.getDaysOfTwoDate(new Date(), DateUtils.parseDate(repayDate, DateUtils.YYYY_MM_DD_SLASH));
            if (dateDiff <= 3) {
                log.debug("距离还款日天数 dateDiff = {}", dateDiff);
                throw new BusinessException(RspEnmu.WALLET_WITHDRAW_IOU_ABOUT_TO_EXPIRE);
            }
        } catch (ParseException e) {
            log.warn("还款日距当前时间比较异常：", e.getMessage());
            throw new BusinessException(RspEnmu.SYSTEM_BUSY);
        }

        log.info("钱包提现借据到期状态校验通过，可以提现", loanIou);
    }

    /**
     * 检查是否有借据
     *
     * @param custNo 客户号
     */
    @Override
    public void checkIou(String custNo) {
        LoanIou loanIou = loanIouMapper.selectIou(custNo);
        if (null != loanIou) {
            throw new BusinessException(RspEnmu.IOU_OUTSTANDING);
        }
    }

    /**
     * 财务点击调用业务系统进行放款
     * @param ids
     * @return
     */
    @Override
    public JSONObject callInitiateTransfer(String ids) {
        JSONObject res = new JSONObject();
        res.put("status", 0);
        int allNum = 0,successNum = 0,failNum = 0,waitNum = 0;
        List<String> idList = new ArrayList<>(Arrays.asList(ids.split(",")));
        allNum = idList.size();
        for (int i = 0; i < idList.size(); i++) {
            String id = idList.get(i).toString();
            log.info("手动放款开始处理:{}",id);
            LoanOrder loanOrder = loanOrderService.getById(id);
            try {
                boolean isDeleted = checkAccountHasDeleteAndInDays(loanOrder);
                if(isDeleted){
                    failNum++;
                }else{
                    payTransService.initiateTransfer(loanOrder.getLoanAmt(), loanOrder.getPayTransSerialNo(), loanOrder.getRecipientCode());
                    // 没有异常往下走，状态3
                    LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId())
                            .chainLoanStatus(LoanOrder.LOAN_STATUS_3_LOAN_PENDING)
                            .chainRemark("loan pending")
                            .chainFreezeDate(loanOrder.getFreezeDate());
                    loanOrderService.updateById(orderUpdate);
                    // 进行查询接口查询
                    JSONObject verifyTransRes = getVerifyTransferRes(loanOrder, false);
                    if (verifyTransRes == null){
                        waitNum++;
                    }
                    // 接收代付结果，代付失败，查询是否重新代付开关，无需则修改提现状态为放款失败，需则另行重新触发
                    String transStatus = verifyTransRes.getString("status");
                    log.info("loanOrder:{} 放款结果：{}", loanOrder.getLoanSerialNo(), transStatus);
                    if (!transStatus.equals("pending") && !transStatus.equals("otp")) {
                        //不是定时任务，则发送消息
                        if(transStatus.equals("success")){
                            successNum ++;
                            mqProduce.sendLoanResultOrderly(loanOrder.getLoanSerialNo(), LoanResultConsume.SUCCESS, loanOrder.getCustNo());
                        }else {
                            failNum++;
                            mqProduce.sendLoanResultOrderly(loanOrder.getLoanSerialNo(), LoanResultConsume.FAILED, loanOrder.getCustNo());
                        }
                    }else{
                        waitNum++;
                    }
                }
            } catch (Exception e) {
                log.error("放款交易处理异常，", e);
                if (e instanceof BusinessException) {
                    BusinessException be = (BusinessException) e;
                    //放款号已存在,不改变订单状态
                    if (RspEnmu.REFERENCE_ALREADY_EXISTS_ON_TRANSFER.getCode().equals(be.getCode())){
                        waitNum++;
                        throw e;
                    }
                    //失败
                    if (RspEnmu.ACCOUNT_NUMBER_IS_INVALID.getMessage().equals(e.getMessage())) {
                        LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId())
                                .chainLoanStatus(LoanOrder.LOAN_STATUS_5_LOAN_FAILED)
                                .chainRemark(be.getDetails())
                                .chainFreezeDate(loanOrder.getFreezeDate());
                        loanOrderService.updateById(orderUpdate);
                        failNum++;
                    }
                    // message=Your balance is not enough to fulfil this request/You cannot initiate third party payouts at this time时，修改loanOrder订单状态为放款等待（loan_status=6）；
                    // 通过定时任务触发+财务触发
                    if (RspEnmu.INSUFFICIENT_WALLET_BALANCE.getMessage().equals(be.getMessage())) {
                        LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId())
                                .chainLoanStatus(LoanOrder.LOAN_STATUS_6_RE_INITIATE_PAY)
                                .chainRemark(be.getDetails())
                                .chainFreezeDate(loanOrder.getFreezeDate());
                        loanOrderService.updateById(orderUpdate);
                        asyncService.loanDelay(loanOrder.getCustNo(),loanOrder.getLoanAmt());
                        waitNum++;
                    }
                }
            }
        }
        res.put("msg","共触发 "+allNum+" 笔订单，放款成功"+successNum+"笔，放款失败"+failNum+"笔，放款等待"+waitNum+"笔");
        return res;
    }

    private boolean checkAccountHasDeleteAndInDays(LoanOrder loanOrder) {
        // 触发放款后，先检查客户状态是否为注销：

        //未注销，则检查订单的创建时间是否超过N天（天数阈值可配置）；

        boolean isDeleted = checkAccountHasDelete(loanOrder);
        if(isDeleted){
            return true;
        }
        boolean isNotInDays = checkAccountHasInDays(loanOrder);
        if(isNotInDays){
            return true;
        }
        return false;
    }

    private boolean checkAccountHasInDays(LoanOrder loanOrder) {
        int days = Integer.parseInt(sysConfigService.selectConfigByKey("loan_order_wait_loan_max_days"));
        int orderDays = DateUtils.daysBetween(loanOrder.getCreateTime(),new Date());
        if(orderDays >= days){
            //超过N天，则修改订单状态为失败（loan_status=5），并触发提现失败短信；
            //已注销，则修改订单状态为失败（loan_status=5），并触发提现失败短信；
            LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId())
                    .chainLoanStatus(LoanOrder.LOAN_STATUS_5_LOAN_FAILED)
                    .chainRemark("订单超过"+days+"天")
                    .chainFreezeDate(loanOrder.getFreezeDate());
            loanOrderService.updateById(orderUpdate);
            //未超过N天，则调用Initiate Transfer重新放款（注：放款流水号不变）；
            noticeSmsService.sendLoanNotice(loanOrder, BizConstants.SMS_TYPE_LOAN_FAILED);
            return true;
        }
        return false;
    }

    private boolean checkAccountHasDelete(LoanOrder loanOrder) {
        RealNameAuth realNameAuth = realNameAuthMapper.selectRealNameAuthByCustNo(loanOrder.getCustNo());
        if(realNameAuth.getState() == RealNameStatusEnum.CANCELLATION.getCode()){
            //已注销，则修改订单状态为失败（loan_status=5），并触发提现失败短信；
            LoanOrder orderUpdate = new LoanOrder().chainId(loanOrder.getId())
                    .chainLoanStatus(LoanOrder.LOAN_STATUS_5_LOAN_FAILED)
                    .chainRemark("用户注销了")
                    .chainFreezeDate(loanOrder.getFreezeDate());
            loanOrderService.updateById(orderUpdate);
            return true;
        }
        return false;
    }

    /**
     * 再次发起通过
     *
     * @param loanOrder
     * @param realNameAuth
     * @param appUser
     * @param creditFlag
     * @return
     */
    private String againTriggerPass(LoanOrder loanOrder, RealNameAuth realNameAuth, AppUser appUser, CreditFlag creditFlag) {
        //成功继续流程
        String error = "";
        try {
            rulePass(loanOrder.getLoanSerialNo());
//            mqProduce.sendLoanApplyOrderly(loanOrder.getLoanSerialNo(), loanOrder.getCustNo());
            loanOrder = loanOrderService.getOneBySerialNo(loanOrder.getLoanSerialNo());
        } catch (Exception e) {
            log.error("提现卡单处理异常-流水号:{}", loanOrder.getLoanSerialNo(), e);
            error = e.getMessage();
        }
        if (loanOrder.getLoanStatus() == LoanOrder.LOAN_STATUS_9_APPROVALING) {
            JSONObject andonContent = customerAndonService.getAndonContent(creditFlag, appUser.getPhone(), realNameAuth.getFullName());
            //触发飞书提醒
            customerAndonService.sendAndonFeishu(creditFlag.getType(), andonContent);
            //触发邮件
            customerAndonService.sendAndonEamil(creditFlag.getType(), andonContent);
            return error;
        }
        return "true";
    }

    private void sendCancelSMS(LoanOrder loanOrder, RealNameAuth realNameAuth, AppUser appUser) {
        JSONObject smsParam = new JSONObject();
        smsParam.put("lastName", realNameAuth.getLastName());
        smsParam.put("amount", loanOrder.getLoanAmt());
        SmsSendData smsData = SmsSendData.builder().noticeType(BizConstants.SMS_NOTICE_TYPE_NOTICE).smsType(BizConstants.ANDON_LOAN).phoneNumber(appUser.getPhone()).countryCode("+234").smsParam(smsParam).build();
        asyncService.sendSms(smsData);
    }

}
